/**
 * jQuery FormChanger Plugin 1.0.3
 *
 * Demo: http://ebanzi.ru/sources/formChanger/index.html
 * Source: http://code.google.com/p/jquery-form-changer/
 *
 * Copyright (c) 2011 Vladimir Axyonov - sketch43@gmail.com
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */
(function($){
	
    var OFormChanger = {
        debug: false,
        basePrefix: 'changer', //@TODO сделать смену перфикса
        settings: {
            form: {
                classes: {
                    base: 'Form'
                }
            },
            text: {
                options: {
                    wraps: 1,
                    wrapsIn: true
                },
                classes: {
                    base: 'Text',
                    wrap: 'Wrap',
                    hover: 'Hover',
                    pressed: 'Pressed',
                    focused: 'Focused',
                    filled: 'Filled',
                    placeholder: 'Place',
                    readonly: 'Readonly',
                    disabled: 'Disabled',
                    invalid: 'Invalid'
                }
            },
            textarea: {
                options: {
                    wraps: 1,
                    wrapsIn: true
                },
                classes: {
                    base: 'Textarea',
                    wrap: 'Wrap',
                    hover: 'Hover',
                    pressed: 'Pressed',
                    focused: 'Focused',
                    filled: 'Filled',
                    placeholder: 'Place',
                    readonly: 'Readonly',
                    disabled: 'Disabled',
                    invalid: 'Invalid'
                }
            },
            button: {
                options: {
                    wraps: 1,
                    wrapsIn: true
                },
                classes: {
                    base: 'Button',
                    wrap: 'Wrap',
                    hover: 'Hover',
                    pressed: 'Pressed',
                    focused: 'Focused',
                    disabled: 'Disabled',
                    title: 'Title'
                }
            },
            check: {
                options: {
                    wraps: 1,
                    wrapsIn: true,
                    flag: ''
                },
                classes: {
                    base: '',
                    wrap: 'Wrap',
                    hover: 'Hover',
                    pressed: 'Pressed',
                    checked: 'Checked',
                    focused: 'Focused',
                    disabled: 'Disabled',
                    state: 'State',
                    invalid: 'Invalid'
                }
            },
            select: {
                options: {
                    wraps: 1,
                    wrapsIn: true,
                    lists: 1,
                    listsIn: true
                },
                classes: {
                    base: 'Select',
                    wrap: 'Wrap',
                    state: 'State',
                    arrow: 'Arrow',
                    list: 'List',
                    listin:'Listin',
                    optgroup: 'Optgroup',
                    option: 'Option',
                    selected: 'Selected',
                    open: 'Open',
                    hover: 'Hover',
                    filled: 'Filled',
                    pressed: 'Pressed',
                    focused: 'Focused',
                    disabled: 'Disabled',
                    invalid: 'Invalid'
                }
            },
            file: {
                options: {
                    wraps: 1,
                    wrapsIn: true
                },
                classes: {
                    base: 'File',
                    wrap: 'Wrap',
                    state: 'State',
                    btn: 'BrowseBtn',
                    hover: 'Hover',
                    pressed: 'Pressed',
                    focused: 'Focused',
                    filled: 'Filled',
                    disabled: 'Disabled',
                    invalid: 'Invalid'
                }
            }
        },
        console: {triggerHandler: function(){}}
    };
    OFormChanger.console.stack = [{type: 'message', method: '.changerDebugger', text: 'Инициализация консоли прошла успешно'}];

    for(var set in OFormChanger.settings){
        OFormChanger.settings[set].classes.base = OFormChanger.basePrefix + OFormChanger.settings[set].classes.base;
    }

    OFormChanger.setDefaults = function(settings){
         
       $.extend(true, this.settings, settings || {});

       if(this.settings.all)
           for(var stg in this.settings) if(stg != 'form') {
               var cacheStgs = {};
               $.extend(true, cacheStgs, settings[stg]);
               $.extend(true, this.settings[stg], settings.all);
               $.extend(true, this.settings[stg], cacheStgs);
           }

       if(settings) OFormChanger.console.stack.push({type: 'message', method: '.setDefaults', text: 'Настройки по умолчанию переопределены'});

    };

    $(function(){
        if(OFormChanger.debug){
            var consoleStack = OFormChanger.console.stack;
            OFormChanger.console = $('<div/>', {'id': 'formChangerDebugConsole','class': 'formChangerDebugConsole'});
            $('body').append(OFormChanger.console);
            OFormChanger.console.changerDebugger().scrollBox();
            for(var i =0, l = consoleStack.length; i < l; i++){
                 OFormChanger.console.triggerHandler(consoleStack[i].type, [consoleStack[i].method,consoleStack[i].text]);
            }
        }
    });

    $.formChanger = OFormChanger;
    
    $.extend($.fn, {
        changerDebugger: function(){
            var self = this;

            this.bind('exсeption message', function(e, method, str){
                self.html(self.html()+'<span class="method">'+method+'</span>:// '+(e.type == 'exсeption'?('<span class="exception">'+str+'</span>'):str)+'<br/>').triggerHandler('innerChange');
                self.data().scrollBox.scrollToBottom();
                
            });

            return this;
        },

        changerDisable: function(disable){
            this.filter(':input').each(function(){
                var self = $(this);
                if(disable){
                    if(!self.attr('disabled'))
                        self.attr({disabled: 'disabled'})
                            .trigger('disable');
                }
                else{
                    if(self.attr('disabled'))
                        self.removeAttr('disabled')
                            .trigger('enable');
                }
            });
            return this;
        },

        changerChecked: function(check){
            this.filter('input:checkbox, input:radio').each(function(){
                var self = $(this);
                if(check){
                    if(!self.attr('checked'))
                        self.attr({checked: 'checked'})
                            .trigger('change');
                }
                else{
                    if(self.attr('checked'))
                        self.removeAttr('checked')
                            .trigger('change');
                }
            });
            return this;
        },

        changerReadonly: function(readonly){
            this.filter('input:text, input:password, textarea').each(function(){
                var self = $(this);
                if(readonly){
                    if(!self.attr('readonly'))
                        self.attr({readonly: 'readonly'})
                            .trigger('readonly');
                }
                else{
                    if(self.attr('readonly'))
                        self.removeAttr('readonly')
                            .trigger('notReadonly');
                }
            });
            return this;
        },

        changerButton: function(classes, options){
            var cls = $.extend($.extend({}, OFormChanger.settings.button.classes), classes || {}),
                opts = $.extend($.extend({}, OFormChanger.settings.button.options), options || {});

            for(var cl in cls) {
                if(cl != 'base') cls[cl] = cls.base + cls[cl];
            }

            if(opts.wraps < 0) opts.wraps = 1;

            this.filter('input:submit, input:button, input:reset, button').each(function(){
                var btn = $(this);

                init(btn);
            });

            function init(btn){
                var hash = {};

                hash.btn = btn;
                hash.data = btn.data().changer = {};

                if(btn.attr('type') == 'reset') resetHandler(hash);

               //создаем обертки
                for(var i = 1; i <= opts.wraps; i++){
                    if(i == 1) {
                        //если первая обертка кладем её в хэш и дату(для работы извне)
                        var wraps = [];
                        btn.wrap($('<span/>', { 'class' : cls.wrap + ' ' + cls.base + 'Wraps' })); //создаем основную обертку
                        hash.wrap = wraps[1] = hash.data.wrap = btn.parent(); //и оборачиваем ей селект
                        if (btn[0].id)
                            hash.wrap.attr('id', cls.base + 'Id-' + btn[0].id);
                        if (btn[0].className){
                            var clsArr = btn[0].className.split(' ');
                            for(var j = 0, l = clsArr.length; j < l; j++)
                                hash.wrap.addClass(cls.base + 'Class-' + clsArr[j]);
                        }
                        continue;
                    }
                    wraps[i] = $('<span/>', { 'class' : cls.wrap + i + ' ' + cls.base + 'Wraps' });
                    if(opts.wrapsIn){
                        wraps[i-1].append(wraps[i]); //создаем остальные обертки
                        wraps[i].append(btn);
                    }
                    else btn.before(wraps[i]);
                }

                btn.before('<span class="'+cls.title+'">'+btn.val()+'</span>');

                btn.css({
                    position: 'absolute',
                    opacity: 0,
                    filter: 'alpha(opacity=0)',
                    top: 0,
                    left: 0,
                    padding: 0,
                    margin: 0,
                    border: 'none'
                });

                btn.height(hash.data.wrap[0].offsetHeight);
                btn.width(hash.data.wrap[0].offsetWidth);

                btn.bind('mouseover mouseout focus focusin blur focusout disable enable mousedown mouseup press unpress', function(e){
                    var self = $(e.target),
                        wrap = hash.wrap;
                        actions(self, wrap, e, cls);
                });

                btn.triggerHandler(btn.context.disabled?'disable':'enable');
            }

            function resetHandler(hash){
                var form = $(hash.btn.context.form);
                if(!form.data().events || !form.data().events.reset)
                    form.bind('reset', function(){
                        var self = this;
                        setTimeout(function(){
                                for(var i = 0; i < self.elements.length; i++){
                                    var el = self.elements[i];
                                    /** для IE 6,7 - при отсуствии атрибута селектед хотя бы у одного опшена они возвращают в качестве вал херню */
                                    if($.browser.msie && el.nodeName.toLowerCase() == 'select' && (!$(el).val() || typeof($(el).val()) != 'string') && el.options.length) el.options[0].selected = true;
                                    /** end - for IE 6,7 */
                                    $(el).trigger('pull');
                                    $(el).trigger('valid');
                                }
                            }, 0);
                    });
            }

            return this;
        },

        changerText: function(classes, options){
             var cls = $.extend($.extend({}, OFormChanger.settings.text.classes), classes || {}),
                opts = $.extend($.extend({}, OFormChanger.settings.text.options), options || {});

            for(var cl in cls) {
                if(cl != 'base') cls[cl] = cls.base + cls[cl];
            }

            if(opts.wraps < 0) opts.wraps = 1;

            this.filter('input:text, input:password').each(function(){
                var inpt = $(this);

                init(inpt);
            });

            function init(inpt){
                var hash = {};

                hash.inpt = inpt;
                hash.data = inpt.data().changer = {};

                //создаем обертки
                for(var i = 1; i <= opts.wraps; i++){
                    if(i == 1) {
                        //если первая обертка кладем её в хэш и дату(для работы извне)
                        var wraps = [];
                        inpt.wrap($('<span/>', { 'class' : cls.wrap + ' ' + cls.base + 'Wraps' })); //создаем основную обертку
                        hash.wrap = wraps[1] = hash.data.wrap = inpt.parent(); //и оборачиваем ей селект
                        if (inpt[0].id)
                            hash.wrap.attr('id', cls.base + 'Id-' + inpt[0].id);
                        if (inpt[0].className){
                            var clsArr = inpt[0].className.split(' ');
                            for(var j = 0, l = clsArr.length; j < l; j++)
                                hash.wrap.addClass(cls.base + 'Class-' + clsArr[j]);
                        }
                        continue;
                    }
                    wraps[i] = $('<span/>', { 'class' : cls.wrap + i + ' ' + cls.base + 'Wraps' });
                    if(opts.wrapsIn){
                        wraps[i-1].append(wraps[i]); //создаем остальные обертки
                        wraps[i].append(inpt);
                    }
                    else inpt.before(wraps[i]);
                }

                hash.wrap.click(function(){
                    if(!inpt.context.disabled) inpt.trigger('focus');
                });

                if(inpt.attr('data-placeholder')) inpt.before($('<span class="' + cls.placeholder + '">' + inpt.attr('data-placeholder') + '</span>'));

                inpt.bind('change pull click mouseover mouseout focus focusin blur focusout disable enable readonly notReadonly mousedown mouseup press unpress invalid valid', function(e){
                    var self = $(e.target),
                        wrap = hash.wrap;
                    switch (e.type) {
                        case 'change':
                        case 'pull':
                            wrap[self.val()?'addClass':'removeClass'](cls.filled);
                            break;
                        default:
                            actions(self, wrap, e, cls);
                            break;
                    }
                });

                inpt.triggerHandler('pull');
                inpt.triggerHandler('focusout');
                inpt.triggerHandler(inpt[0].disabled?'disable':'enable');
                inpt.triggerHandler(inpt.attr('readonly')?'readonly':'notReadonly');
            }

            return this;
        },

        changerFile: function(classes, options){
            var cls = $.extend($.extend({}, OFormChanger.settings.file.classes), classes || {}),
                opts = $.extend($.extend({}, OFormChanger.settings.file.options), options || {});

            for(var cl in cls) {
                if(cl != 'base') cls[cl] = cls.base + cls[cl];
            }

            if(opts.wraps <= 0) opts.wraps = 1;

            this.filter('input:file').each(function(){
                var inpt = $(this);

                init(inpt);
            });

            function init(inpt){
                var hash = {};

                hash.inpt = inpt;
                hash.data = inpt.data().changer = {};

                //создаем обертки
                for(var i = 1; i <= opts.wraps; i++){
                    if(i == 1) {
                        //если первая обертка кладем её в хэш и дату(для работы извне)
                        var wraps = [];
                        inpt.wrap($('<span/>', { 'class' : cls.wrap + ' ' + cls.base + 'Wraps' })); //создаем основную обертку
                        hash.wrap = wraps[1] = hash.data.wrap = inpt.parent(); //и оборачиваем ей селект
                        if (inpt[0].id)
                            hash.wrap.attr('id', cls.base + 'Id-' + inpt[0].id);
                        if (inpt[0].className){
                            var clsArr = inpt[0].className.split(' ');
                            for(var j = 0, l = clsArr.length; j < l; j++)
                                hash.wrap.addClass(cls.base + 'Class-' + clsArr[j]);
                        }
                        continue;
                    }
                    wraps[i] = $('<span/>', { 'class' : cls.wrap + i + ' ' + cls.base + 'Wraps' });
                    if(opts.wrapsIn){
                        wraps[i-1].append(wraps[i]); //создаем остальные обертки
                        wraps[i].append(inpt);
                    }
                    else inpt.before(wraps[i]);
                }

                hash.state = $('<span/>', {'class':cls.state});
                hash.btn = $('<span/>', {'class':cls.btn});
                hash.inpt.before(hash.state);
                hash.inpt.before(hash.btn);

                inpt.bind('change pull click mouseover mouseout focus focusin blur focusout disable enable mousedown mouseup press unpress invalid valid', function(e){
                    var self = $(e.target),
                        wrap = hash.wrap;
                    switch (e.type) {
                        case 'change':
                        case 'pull':
                            wrap[self.val()?'addClass':'removeClass'](cls.filled);

                            if(inpt.attr('data-placeholder')) hash.state.text(inpt.attr('data-placeholder'));

                            var val = val1 = hash.inpt.val();
                            if(val) {
                                val = val.substring(val.lastIndexOf('\\')+1);
                                hash.state.text(val);
                                hash.inpt.attr('title',val1);
                            }

                            break;
                        default:
                            actions(self, wrap, e, cls);
                            break;
                    }
                });

                inpt.triggerHandler('pull');
                inpt.triggerHandler('focusout');
                inpt.triggerHandler(inpt.context.disabled?'disable':'enable');
                inpt.triggerHandler(inpt.attr('readonly')?'readonly':'notReadonly');
            }

            return this;
        },

        changerCheck: function(classes, options){
            this.filter('input:radio, input:checkbox').each(function(){
                var check = $(this);

                init(check);
            });

            function init(check){
                var hash = {},
                    type = check.attr('type').substring(0,1).toUpperCase()+check.attr('type').substring(1);

                var cls = $.extend($.extend({}, OFormChanger.settings.check.classes), classes || {}),
                    opts = $.extend($.extend({}, OFormChanger.settings.check.options), options || {});
                    lClasses = {};

                cls.base += type;

                for(var cl in cls) {
                    if(cl != 'base') cls[cl] = cls.base + cls[cl];
                }

                if(opts.wraps < 0) opts.wraps = 1;

                hash.check = check;
                hash.data = check.data().changer = {};

                //создаем обертки
                for(var i = 1; i <= opts.wraps; i++){
                    if(i == 1) {
                        //если первая обертка кладем её в хэш и дату(для работы извне)
                        var wraps = [];
                        check.wrap($('<span/>', { 'class' : cls.wrap + ' ' + cls.base + 'Wraps' })); //создаем основную обертку
                        hash.wrap = wraps[1] = hash.data.wrap = check.parent(); //и оборачиваем ей селект
                        if (check[0].id)
                            hash.wrap.attr('id', cls.base + 'Id-' + check[0].id);
                        if (check[0].className){
                            var clsArr = check[0].className.split(' ');
                            for(var j = 0, l = clsArr.length; j < l; j++)
                                hash.wrap.addClass(cls.base + 'Class-' + clsArr[j]);
                        }
                        continue;
                    }
                    wraps[i] = $('<span/>', { 'class' : cls.wrap + i + ' ' + cls.base + 'Wraps' });
                    if(opts.wrapsIn){
                        wraps[i-1].append(wraps[i]); //создаем остальные обертки
                        wraps[i].append(check);
                    }
                    else check.before(wraps[i]);
                }

                hash.state = $('<span/>', {'class':cls.state});

                check.css({
                        opacity: 0,
                        margin: 0,
                        padding: 0,
                        outline: 'none',
                        'outline-width': 0,
                        width: '100%',
                        height: '100%',
                        position: 'relative',
                        'z-index': 10000
                    })
                    .before(hash.state.text(opts.flag));

                check.bind('change pull click mouseover mouseout focus focusin blur focusout disable enable mousedown mouseup press unpress invalid valid', function(e){
                    var self = $(e.target),
                        wrap = hash.wrap,
                        checked = e.target.checked;

                    switch (e.type) {
                        case 'change':
                        case 'click':
                        case 'pull':
                            wrap[checked?'addClass':'removeClass'](cls.checked);
                            //self.data().changer.label[check?'addClass':'removeClass'](lClasses.checkedClass);
                            if(checked && type == 'Radio'){
                                var uncheckEls = $('input:radio[name='+self.attr('name')+']').not(self);
                                uncheckEls.each(function(){
                                    var self = $(this);
                                    if(self.data().changer)
                                        self.data().changer.wrap.removeClass(cls.checked);
                                    //if(self.data().changer && self.data().changer.label)
                                    //	self.data().changer.label.removeClass(lClasses.checkedClass);
                                });
                            }
                            break;
                        default:
                            actions(self, wrap, e, cls); //, lClasses
                            break;
                    }
                });

                check.triggerHandler('pull');
                check.triggerHandler(check.context.disabled?'disable':'enable');
            }

            return this;
        },

        changerTextarea: function(classes, options){
            var bodyEl = $('body'),
                cls = $.extend($.extend({}, OFormChanger.settings.textarea.classes), classes || {}),
                opts = $.extend($.extend({}, OFormChanger.settings.textarea.options), options || {});

            for(var cl in cls) {
                if(cl != 'base') cls[cl] = cls.base + cls[cl];
            }

            if(opts.wraps < 0) opts.wraps = 1;

            this.filter('textarea').each(function(){
                var inpt = $(this);

                init(inpt);
            });

            function init(inpt){
                var hash = {};

                hash.inpt = inpt;
                hash.data = inpt.data().changer = {};

                //создаем обертки
                for(var i = 1; i <= opts.wraps; i++){
                    if(i == 1) {
                        //если первая обертка кладем её в хэш и дату(для работы извне)
                        var wraps = [];
                        inpt.wrap($('<span/>', { 'class' : cls.wrap + ' ' + cls.base + 'Wraps' })); //создаем основную обертку
                        hash.wrap = wraps[1] = hash.data.wrap = inpt.parent(); //и оборачиваем ей селект
                        if (inpt[0].id)
                            hash.wrap.attr('id', cls.base + 'Id-' + inpt[0].id);
                        if (inpt[0].className){
                            var clsArr = inpt[0].className.split(' ');
                            for(var j = 0, l = clsArr.length; j < l; j++)
                                hash.wrap.addClass(cls.base + 'Class-' + clsArr[j]);
                        }
                        continue;
                    }
                    wraps[i] = $('<span/>', { 'class' : cls.wrap + i + ' ' + cls.base + 'Wraps' });
                    if(opts.wrapsIn){
                        wraps[i-1].append(wraps[i]); //создаем остальные обертки
                        wraps[i].append(inpt);
                    }
                    else inpt.before(wraps[i]);
                }

                hash.list = wraps[wraps.length-1].addClass(cls.base+'InnerWrap');
                hash.listin = inpt;

                if(inpt.attr('data-placeholder')) inpt.before($('<span class="' + cls.placeholder + '">' + inpt.attr('data-placeholder') + '</span>').click(function(){
                    if(!inpt.context.disabled) inpt.trigger('focus');
                }));

                hash.listin.scrollBox();

                inpt.bind('change pull click mouseover mouseout focus focusin blur focusout disable enable readonly notReadonly mousedown mouseup press unpress reset input keyup invalid valid', function(e){
                    var self = $(e.target),
                        wrap = hash.wrap;
                    switch (e.type) {
                        case 'pull':
                        case 'change':
                            wrap[self.val()?'addClass':'removeClass'](cls.filled);
                            break;
                        case 'input':
                            //hash.listin.data().scrollBox.scrollToBottom();
                        default:
                            actions(self, wrap, e, cls);
                            break;
                    }
                });

                inpt.triggerHandler('pull');
                inpt.triggerHandler('focusout');
                inpt.triggerHandler(inpt[0].disabled?'disable':'enable');
                inpt.triggerHandler(inpt.attr('readonly')?'readonly':'notReadonly');


            }

            return this;
        },

        changerSelect: function(classes, options){
            var bodyEl = $('body'),
                cls = $.extend($.extend({}, OFormChanger.settings.select.classes), classes || {}),
                opts = $.extend($.extend({}, OFormChanger.settings.select.options), options || {});

            for(var cl in cls) {
                if(cl != 'base') cls[cl] = cls.base + cls[cl];
            }

            if(opts.wraps < 0) opts.wraps = 1;

            this.filter('select').each(function(){
                var sel = $(this);
                init(sel);
            });

            function init(sel){
                var hash = {};

                hash.sel = sel;
                hash.data = sel.data().changer = {};
                hash.sel.multiple = sel.attr('multiple');
                hash.sel.sizeble = hash.sel.attr('size')<2?0:hash.sel.attr('size');

                //создаем обертки
                for(var i = 1; i <= opts.wraps; i++){
                    if(i == 1) {
                        //если первая обертка кладем её в хэш и дату(для работы извне)
                        var wraps = [];
                        sel.wrap($('<span/>', { 'class' : cls.wrap + ' ' + cls.base + 'Wraps' })); //создаем основную обертку
                        hash.wrap = wraps[1] = hash.data.wrap = sel.parent(); //и оборачиваем ей селект
                        if (sel[0].id)
                            hash.wrap.attr('id', cls.base + 'Id-' + sel[0].id);
                        if (sel[0].className){
                            var clsArr = sel[0].className.split(' ');
                            for(var j = 0, l = clsArr.length; j < l; j++)
                                hash.wrap.addClass(cls.base + 'Class-' + clsArr[j]);
                        }
                        continue;
                    }
                    wraps[i] = $('<span/>', { 'class' : cls.wrap + i + ' ' + cls.base + 'Wraps' });
                    if(opts.wrapsIn){
                        wraps[i-1].append(wraps[i]); //создаем остальные обертки
                        wraps[i].append(sel);
                    }
                    else sel.before(wraps[i]);
                }

                //создаем элементы выбраного значения и списка опшенов
                hash.state = $('<span/>', {'class': cls.state, role: 'State'});
                hash.arrow = $('<span/>', {'class': cls.arrow, role: 'Arrow'});

                //добавляем созданые элементы стрелки и состояния в обертку
                if(opts.wrapsIn){
                    wraps[wraps.length-1]
                        .append(hash.state)
                        .append(hash.arrow);
                }
                else{
                    hash.wrap
                        .append(hash.state)
                        .append(hash.arrow);
                }

                //создаем и добавляем обертки листа
                for(var i = 1; i <= opts.lists; i++){
                    if(i == 1) {
                        var lists = [];
                        hash.list = lists[1] = $('<span/>', {'class': cls.list}); //создаем основную обертку листа
                        hash.wrap.append(hash.list); //и добавляем ее в обертку всего селекта
                        continue;
                    }
                    lists[i] = $('<span/>', { 'class' : cls.list+i });
                    if(opts.listsIn) lists[i-1].append(lists[i]); //создаем остальные обертки листа
                    else hash.list.append(lists[i]);
                }

                //переносим селект в конец обертки, чтобы не мозолил глаза в коде
                hash.list.after(sel);

                //создаем и добавялем внутренний лист
                hash.listin = $('<span/>', {'class': cls.listin, role: 'Listin'});
                if(opts.listsIn) lists[lists.length-1].append(hash.listin);
                else hash.list.append(hash.listin);

                //если есть атрибут сайз, инитим лист
                /*if(hash.sel.sizeble || hash.sel.multiple) {
                    if(hash.sel.sizeble) hash.wrap.addClass('selectSizeble').addClass('selectSize' + hash.sel.attr('size'));
                    else hash.wrap.addClass('selectMultiple');
                    regenerateList(hash);
                    hash.sel.sizeble?setSelected(hash):initSelectedMultyple(hash);
                    checkToScroll(hash);
                }*/

                hash.listin.scrollBox();

                /**
                    навешиваем обработчики событий
                */
                hash.wrap.bind('change pull click mouseover mouseout focus focusin focusout disable enable mousedown mouseup press unpress invalid valid', function(e){
                    // @TODO разобраться с пустыми селектами
                    if(!sel[0].options.length || (sel[0].disabled && e.type != 'disable' && e.type != 'change')) return false;
                    var self = $(e.target),
                        selfRole = self.attr('role'),
                        isList = self.parents('.'+cls.list).length,
                        wrap = !isList?hash.wrap:self;

                    switch (e.type) {
                        case 'click':
                            if(!isList){
                                // IE - нажатие на внутренние элементы
                                if($.browser.msie && self != wrap) wrap.focus();

                                if(!wrap.is('.'+cls.open)){
                                    openSelect(hash);
                                }
                                else{
                                    wrap.removeClass(cls.open);
                                    //closeSelect();
                                }
                                bodyEl.mousedown(function(){
                                    bodyEl.bind('mouseup', closeSelect);
                                });
                            }
                            else if(selfRole == 'Option'){
                                if(!hash.sel.multiple){
                                    self.data().changerOption.attr({'selected':'selected'});

                                    if(hash.sel.sizeble) setSelected(hash);
                                    else {
                                        hash.wrap.removeClass(cls.open);
                                        hash.sel.trigger('change');
                                        hash.sel.trigger('focusout');
                                    }

                                }
                                else{
                                    var opts = hash.sel[0].options;
                                    if(!e.ctrlKey){
                                        for(var i = 0, l = opts.length; i < l; i++){
                                            opts[i].selected = false;
                                        }
                                        self.data().changerOption.attr({'selected':'selected'});
                                        setSelected(hash);
                                    }
                                    else {
                                        if(self.data().changerOption[0].selected == true){
                                            self.data().changerOption[0].selected = false;
                                            setSelectedMultiple(hash, self, false);
                                        }
                                        else {
                                            self.data().changerOption[0].selected = true;
                                            setSelectedMultiple(hash, self, true);
                                        }
                                    }
                                }
                            }
                            break;
                        case 'change':
                        case 'pull':
                            hash.state.text($(sel[0].options[sel[0].selectedIndex]).text());
                            hash.wrap[(sel[0].value?'add':'remove')+'Class'](cls.filled);

                            break;
                        case 'mouseover':
                            if(!isList) actions(self, self, e, cls);
                            actions(self, wrap, e, cls);
                            break;
                        case 'mouseout':
                            if(!isList) actions(self, self, e, cls);
                            actions(self, wrap, e, cls);
                            break;
                        case 'focus':
                        case 'focusin':
                            if(!self.hasClass(cls.wrap)) return false;
                            actions(self, wrap, e, cls);
                            break;
                        case 'blur':
                        case 'focusout':
                            // для осла ретурним фолс, когда фокус перешел к чилде
                            if(e.originalEvent && $(e.originalEvent.toElement).parents('.'+cls.wrap).length) return false;
                            actions(self, wrap, e, cls);
                        default:
                            actions(self, wrap, e, cls);
                            break;
                    }
                });

                //скрываем реальный селект и сбрасываем табиндекс, чтобы не фокусился
                sel.attr({'tabindex':'-1'}).css({'position':'absolute', 'left':'-5000px'});

                //ставим обертке табиндекс, чтобы она начала получать фокус
                hash.wrap.attr({'tabindex':'0'});

                //вызываем функцию экшенов, чтобы инициировать начально состояние
                actions(sel, hash.wrap, {type: sel.context.disabled?'disable':'enable'}, cls);
                sel.trigger('pull');
            }

            function openSelect(hash){
                regenerateList(hash);
                setSelected(hash);
                hash.wrap.addClass(cls.open);
                hash.listin.triggerHandler('innerChange');
                scrollToSelected(hash);
            }

            function regenerateList(hash){
                hash.listin.empty();

                var opts = hash.sel.children();

               for(var i = 0, l = opts.length; i < l; i++){
                    var opt = $(opts[i]),
                        optElement = opt.data().changerElement;
                    if(opt[0].nodeName.toLowerCase() == 'optgroup'){
                        createOptgroup(opt, hash);
                    }
                    else if(optElement){
                        optElement.data().changerOption = opt;
                        hash.listin.append(optElement);
                    }
                    else createOption(opt, hash.listin, hash);
                }

                if(!hash.sel.multiple && opts.length && hash.sel[0].selectedIndex < 0) {
                    opts[0].selected = true;
                }
            }

            function setSelected(hash){
                var opts = hash.sel[0].options,
                    optElement = $(opts[hash.sel[0].selectedIndex]).data().changerElement;

                hash.listin.find('span').removeClass(cls.selected+'Option');
                optElement.addClass(cls.selected+'Option');
            }

            function setSelectedMultiple(hash, self, act){
                if(act) self.addClass(cls.selected+'Option');
                else self.removeClass(cls.selected+'Option');
            }

            function initSelectedMultyple(hash){
                var opts = hash.sel[0].options;

                for(var i = 0, l = opts.length; i < l; i++){
                    var opt = $(opts[i]),
                        optElement = opt.data().changerElement;
                    if(opt[0].selected) optElement.addClass(cls.selected+'Option');
                }
            }

            function scrollToSelected(hash){
                var opts = hash.sel[0].options;

                if(hash.listin.data().scrollBox){ // если есть скролбар, скролим к селектнутому опшену
                    var optElement = $(opts[hash.sel[0].selectedIndex]).data().changerElement;

                    hash.listin.data().scrollBox.scrollTo(optElement[0].offsetTop);
                }
            }

            function createOptgroup(group, hash){
                var optgroup = $('<span/>', {'class' : cls.optgroup, role: 'Optgroup'}),
                    title = $('<span/>', {'class' : cls.base+'Title', 'role': 'Title'}).text(group.attr('label'));
                optgroup.append(title);
                hash.listin.append(optgroup);
                // тоже самое делаем для вложеных для них опшенов
                for(var i = 0, l = group.children().length; i < l; i++){
                    var opt = $(group.children().eq(i));
                    createOption(opt, optgroup, hash);
                }
            }

            function createOption(opt, optgroup, hash){
                var option = $('<span/>', {'class': cls.option, role: 'Option'}).text(opt.text());
                option.data().changerOption = opt;
                opt.data().changerElement = option;
                optgroup.append(option);
            }

            function closeSelect(e){
                var t = $(e.target);
                $('.'+cls.open).not(t).not(t.parents('.'+cls.open)).removeClass(cls.open);
                $('.'+cls.focused).not(t).not(t.parents('.'+cls.focused)).removeClass(cls.focused);
                if(!$('.'+cls.open).length && !$('.'+cls.focused).length) bodyEl.unbind('mouseup', closeSelect);
            }

            return this;
        },

        applyChanger: function(settings){
           var stgs =  $.extend(true, $.extend({}, OFormChanger.settings), settings || {});

           if(settings && settings.all)
                for(var stg in stgs) if(stg != 'form') {
                    var cacheStgs = {};
                    $.extend(true, cacheStgs, settings[stg]);
                    $.extend(true, stgs[stg], settings.all);
                    $.extend(true, stgs[stg], cacheStgs);
                 }


            if(this.length > 0){
                apply(this);
            }

            function apply(elements) {
                elements = elements || 0;
                for(var i = 0, l = elements.length; i < l; i++){
                    var element = $(elements[i]);

                    if(element[0].nodeName.toLowerCase() == 'form'){
                      element.addClass(stgs.form.classes.base);
                      apply(element[0].elements);
                      continue;
                    }

                    switch(element[0].nodeName.toLowerCase()) {
                        case 'input':
                            switch(element.attr('type').toLowerCase()){
                                case 'text':
                                case 'password':
                                    element.changerText(stgs.text.classes, stgs.text.options);
                                    break;
                                case 'submit':
                                case 'reset':
                                case 'button':
                                    element.changerButton(stgs.button.classes, stgs.button.options);
                                    break;
                                case 'checkbox':
                                case 'radio':
                                    element.changerCheck(stgs.check.classes, stgs.check.options);
                                    break;
                                case 'file':
                                    element.changerFile(stgs.file.classes, stgs.file.options);
                                    break;
                                case 'hidden':
                                    break;
                                default:
                                    OFormChanger.console.triggerHandler('message', ['.applyChanger', 'Элемент типа  '+ element.attr('type') +' не поддерживается. Проигнорирован']);
                            }
                            break;
                        case 'textarea':
                            $(element).changerTextarea(stgs.textarea.classes, stgs.textarea.options);
                            break;
                        case 'select':
                            $(element).changerSelect(stgs.select.classes, stgs.select.options);
                            break;
                        default:
                            OFormChanger.console.triggerHandler('message', ['.applyChanger', 'Элемент  '+ element[0].nodeName +' не является элементом формы. Проигнорирован']);
                    }
                }
            }

           return this;
        },

        scrollBox: function(classes, options){

            var cls = $.extend({
                    base: 'scrollBox',
                    innerBox: 'Inner',
                    bar: 'Bar',
                    barUp: 'BarUpArrow',
                    barDown: 'BarDownArrow',
                    barArea: 'BarArea',
                    barScroller: 'BarScroller'
                }, classes || {}),
                opts = $.extend({
                    wraps: 1,
                    scrollStep: 15,
                    wrapper: null,
                    scrollerDecor: 3
                }, options || {}),
                bodyElement = $(document.body);

            for(var cl in cls)
                if(cl != 'base') cls[cl] = cls.base + cls[cl];

            this.each(function(){
                var box = $(this),
                    OBox = new Box($(this));

                OBox.init();
            });

            function Box(box){

                var self = this;

                this.element = !opts.wrapper?$('<span />', {'class': cls.base}):opts.wrapper;
                this.innerBox = box;

                this.init = function(){

                    if (this.innerBox[0].id)
                        this.element.attr('id', cls.base + 'Id-' + this.innerBox[0].id);
                    if (this.innerBox[0].className){
                        var clsArr = this.innerBox[0].className.split(' ');
                        for(var j = 0, l = clsArr.length; j < l; j++)
                            this.element.addClass(cls.base + 'Class-' + clsArr[j]);
                    }

                    this.innerBox.addClass(cls.innerBox);

                    this.innerBox.before(this.element);
                    this.element.append(this.innerBox);

                    this.check();

                    this.innerBox.bind('innerChange input', function(){
                        self.check();
                    });

                    this.innerBox.data().scrollBox = {
                        wrap: this.element,
                        scrollTo: function(to){
                            if(self.bar) self.bar.scrollTo(to);
                        },
                        scrollToBottom: function(){
                            if(self.bar) self.bar.scrollTo(self.innerBoxScrHgt+100);
                        }
                    };

                };

                this.setHeights = function(){
                    this.elementHeight = this.element.height();
                    if(this.innerBox.height() != this.elementHeight) {
                        this.innerBox.height('auto');
                        this.innerBox.height(this.elementHeight);
                    }
                    this.innerBoxScrHgt = this.innerBox[0].scrollHeight;
                };

                this.createBar = function(){
                    this.bar = new ScrollBar(this);
                    this.bar.appendTo(this.element).init();
                };

                this.removeBar = function(){
                    if(this.bar) this.bar.remove();
                };

                this.check = function(){
                    this.setHeights();
                    if(this.elementHeight >= this.innerBoxScrHgt){
                        this.removeBar();
                        this.element.removeClass('withScroll');
                    }
                    else {
                        !this.bar?this.createBar():this.bar.recalc();
                        if(!this.element.is('.withScroll')) this.element.addClass('withScroll');
                    }
                };
            }

            function ScrollBar(box){

                var self = this;

                this.box = box;
                this.scrollBar = $('<span/>', {'class': cls.bar, role: cls.bar});
                this.scrollBarUp = $('<span/>', {'class': cls.barUp, role: cls.barUp});
                this.scrollBarDown = $('<span/>', {'class': cls.barDown, role: cls.barDown});
                this.scrollBarArea = $('<span/>', {'class': cls.barArea, role: cls.barArea});
                this.scrollBarScroller = $('<span/>', {'class': cls.barScroller, role: cls.barScroller});

                this.init = function(){
                    for(var i = 1, l = opts.scrollerDecor; i <= l; i++)
                        this.scrollBarScroller.append($('<span />', {'class': cls.barScroller+'Decor'+i}));

                    this.scrollBar.append(this.scrollBarUp.add(this.scrollBarArea.append(this.scrollBarScroller)).add(this.scrollBarDown));

                    this.setHeights();
                };

                this.scrollTo = function(to){
                    this.box.innerBox[0].scrollTop = to || 0;
                    this.recalc();
                };

                this.remove = function(){
                    this.scrollBar.remove();
                    this.box.bar = null;
                };

                this.appendTo = function(element){
                    element.append(this.scrollBar);
                    return this;
                };

                this.setHeights = function(){
                    this.scrollBarUp.hgt = this.scrollBarUp.height();
                    this.scrollBarDown.hgt = this.scrollBarDown.height();
                    this.scrollBarArea.hgt = this.scrollBar.height() - (this.scrollBarUp.hgt + this.scrollBarDown.hgt);

                    this.scrollBarArea.height(this.scrollBarArea.hgt);

                    this.setScrollerHeight();
                    this.setScrollerEvents();
                };

                this.recalc = function(){
                    this.setScrollerHeight();
                    this.moveScroller();
                };

                this.setScrollerHeight = function(){
                    //this.box.innerBox.height(); // дергаем высоту, чтобы ослы < 8 отрендерили элемент до того как будет считаться
                    this.scrollBarScroller.height(parseInt(this.box.elementHeight/this.box.innerBoxScrHgt*this.scrollBarArea.hgt));
                    this.scrollBarScroller.hgt = this.scrollBarScroller.height();
                };

                this.setScrollerEvents = function(){
                    this.box.element.bind('sMouseScroll', function(e){
                        self.box.innerBox.scrollTop(self.box.innerBox[0].scrollTop+e.delta*opts.scrollStep);
                        self.moveScroller();
                        return false;
                    });
                    this.scrollBarUp.click(function(){
                        self.scrollUp();
                    }).mousedown(function(){

                    }).mouseup(function(){

                    });
                    this.scrollBarDown.click(function(){
                        self.scrollDown();
                    }).mousedown(function(){

                    }).mouseup(function(){

                    });
                    this.scrollBarScroller.mousedown(this.scrollerMouseDown);
                }

                this.scrollerMouseDown = function(e){
                    self.scrollBarScroller.startCord = e.pageY;
                    self.scrollBarScroller.startScrollCord = parseInt(self.scrollBarScroller.css('top'));
                    bodyElement.bind('mousemove', self.dragDropScroller)
                                .mouseup(function(){
                                    bodyElement.unbind('mousemove', self.dragDropScroller);
                                });
                    return false;
                };

                this.scrollUp = function(){
                    this.box.innerBox.scrollTop(this.box.innerBox[0].scrollTop-opts.scrollStep);
                    this.moveScroller();
                };

                this.scrollDown = function(){
                    this.box.innerBox.scrollTop(this.box.innerBox[0].scrollTop+opts.scrollStep);
                    this.moveScroller();
                };

                this.dragDropScroller = function(e){
                    var curCord = e.pageY,
                        delta = self.scrollBarScroller.startCord - curCord,
                        scrollerTop = Math.min(Math.max(self.scrollBarScroller.startScrollCord-delta, 0), self.scrollBarArea.hgt - self.scrollBarScroller.hgt);

                    self.scrollBarScroller.css({top: scrollerTop});

                    self.box.innerBox.scrollTop(scrollerTop*(self.box.innerBoxScrHgt-self.box.elementHeight)/(self.scrollBarArea.hgt-self.scrollBarScroller.hgt));
                    return false;
                };

                this.moveScroller = function(){
                    var scrollerTop = ((this.scrollBarArea.hgt-this.scrollBarScroller.hgt)*this.box.innerBox[0].scrollTop)/(this.box.innerBoxScrHgt-this.box.elementHeight);
                    this.scrollBarScroller.css({top: scrollerTop});
                };
            }

            return this;

        }
    });

    $.event.special.sMouseScroll = {
		setup: function() {
			if (this.addEventListener)
				this.addEventListener('DOMMouseScroll', $.event.special.sMouseScroll.handler, false);
			this.onmousewheel = $.event.special.sMouseScroll.handler;
		},
		teardown: function() {
			if (this.removeEventListener)
				this.removeEventListener('DOMMouseScroll', $.event.special.sMouseScroll.handler, false);
			this.onmousewheel = null;
		},
		handler: function( event ) {
			event = $.event.fix(event || window.event);
			event.type = "sMouseScroll";
			event.delta = 0;
			event.delta = event.wheelDelta/-40 || event.detail;
			$.event.handle.apply( this, [event] );
			return ;
		}
	};

    if (/*@cc_on!@*/false)
		$.event.special.input = {
			setup: function(d){
				if (d){
					$(this).bind('keyup.IE drop.IE cut.IE paste.IE', d, $.event.special.input._handler);
				} else {
					$(this).bind('keyup.IE drop.IE cut.IE paste.IE', $.event.special.input._handler);
				}
			},
			teardown: function(){
				$(this).unbind('keyup.IE drop.IE cut.IE paste.IE', $.event.special.input._handler);
			},
			_handler: function(e){
				var t = this,
					a = arguments;
				if (e.type !== 'keyup'){
					setTimeout(function(){
						$.event.special.input.handler.apply(t, a);
					},0);
				} else {
					$.event.special.input.handler.apply(t, a);
				}
			},
			handler: function(e){
				var el = e.target,
					name = el.tagName.toLocaleLowerCase();
				if (name !== 'input' && name !== 'textarea') return;
				if (name === 'input' && (el.type === 'button' || el.type === 'checkbox' || el.type === 'radio' || el.type === 'hidden' || el.type === 'image' || el.type === 'reset' || el.type === 'submit')) return;
				var symbolKey = (e.which >= 48 && e.which <= 90) || (e.which >= 96 && e.which <= 111) || (e.which >= 186 && e.which <= 222) || e.which === 32 || e.which === 13,
					deleteKey = e.which === 46 || e.which === 8;
				if (e.type === 'keyup' && (!symbolKey || (symbolKey && e.ctrlKey)) && !deleteKey) return;
				if (el._value !== el.value || e.type === 'drop' || e.type === 'paste' || e.type === 'cut'){
					e.type = 'input';
					$.event.handle.apply(this, arguments);
					el._value = el.value;
				}
			}
		};

    function actions(self, wrap, e, cls){
		var role = wrap.attr('role') || '';
		switch (e.type) {
			case 'mouseover':
				wrap.addClass(cls.hover+role);
				break;
			case 'mouseout':
				wrap.removeClass(cls.hover+role);
				if(wrap.hasClass(cls.pressed+role)) self.trigger('unpress');
				break;
			case 'focus':
			case 'focusin':
				wrap.addClass(cls.focused+role);
				break;
			case 'blur':
			case 'focusout':
				wrap.removeClass(cls.focused);
				break;
			case 'disable':
				wrap.addClass(cls.disabled);
				break;
			case 'enable':
				wrap.removeClass(cls.disabled);
				break;
			case 'mousedown':
				self.trigger('press');
				break;
			case 'mouseup':
				self.trigger('unpress');
				break;
			case 'press':
				wrap.addClass(cls.pressed+role);
				break;
			case 'unpress':
				wrap.removeClass(cls.pressed+role);
				break;
			case 'readonly':
				wrap.addClass(cls.readonly);
				break;
			case 'notReadonly':
				wrap.removeClass(cls.readonly);
				break;
            case 'invalid':
				wrap.addClass(cls.invalid);
				break;
            case 'valid':
				wrap.removeClass(cls.invalid);
				break;
		}
	}

})(jQuery);